#include "cpofsm.h"
#include "cpodata.h"
#include "es_print.h"
#include "cpotimer.h"
#include "cpolist.h"
#include "cpoprotocal.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

extern struct single_point syspoint_hd;
extern cpo_dev_t *cpo_dev;
extern int mq_id;
extern struct _subfsm_sn_t sf_sn;

/* 状态机头 */
extern cpo_fsm_head_l *cpo_subfsm_hd;

/* 子状态机的初始态所对应的几种转移 */
cpo_fsmtrans_t subfsm_init[] = {
    {EV_SUBSYS_START,   &cpo_fsmpre_null,       &fsm_init_2ivwpwait,    &fsm_init_2ivwpwait_end,    S_IVWP_WAIT},
    {EV_SYS_STOP,       &fsm_init_2init_pre,    &cpo_fsmaction_null,    &fsm_init_2init_end,        S_SUBSYS_INIT},
    {EV_SUBSYS_STOP,    &fsm_init_2init_pre,    &cpo_fsmaction_null,    &fsm_init_2init_end,        S_SUBSYS_INIT},
};

/* 子状态机的等待水阀状态所对应的几种转移 */
cpo_fsmtrans_t subfsm_ivwpwait[] = {
    {EV_WP_ON,          &fsm_ivwpwait_2chwait_pre,  &fsm_ivwpwait_2chwait,  &fsm_ivwpwait_2chwait_end,  S_CH_WAIT},
    {EV_SYS_STOP,       &fsm_ivwpwait_2init_pre,    &fsm_ivwpwait_2init,    &fsm_ivwpwait_2init_end,    S_SUBSYS_INIT},
    {EV_SUBSYS_STOP,    &fsm_ivwpwait_2init_pre,    &fsm_ivwpwait_2init,    &fsm_ivwpwait_2init_end,    S_SUBSYS_INIT},
};

/* 子状态机的等待冷机状态所对应的几种转移 */
cpo_fsmtrans_t subfsm_chwait[] = {
    {EV_CH_ON,          &fsm_chwait_2idle_pre,      &cpo_fsmaction_null,    &cpo_fsmaction_null,        S_SUBSYS_IDLE},
    {EV_SYS_STOP,       &fsm_chwait_2ivwpwait_pre,  &fsm_chwait_2ivwpwait,  &fsm_chwait_2ivwpwait_end,  S_IVWP_WAIT},
    {EV_SUBSYS_STOP,    &fsm_chwait_2ivwpwait_pre,  &fsm_chwait_2ivwpwait,  &fsm_chwait_2ivwpwait_end,  S_IVWP_WAIT},
};

/* 子状态机的idle状态所对应的几种转移 */
cpo_fsmtrans_t subfsm_idle[] = {
    {EV_SYS_STOP,       &cpo_fsmpre_null,   &cpo_fsmaction_null,    &fsm_idle_2chwait_end,  S_CH_WAIT},
    {EV_SUBSYS_STOP,    &cpo_fsmpre_null,   &cpo_fsmaction_null,    &fsm_idle_2chwait_end,  S_CH_WAIT},
};

cpo_fsmsets_t cpo_subfsmset[] = {
    {},
    {},
    {},
    {},
    {},
    {"S_SUBSYS_INIT",   sizeof(subfsm_init)/sizeof(subfsm_init[0]),         subfsm_init},
    {"S_IVWP_WAIT",     sizeof(subfsm_ivwpwait)/sizeof(subfsm_ivwpwait[0]), subfsm_ivwpwait},
    {"S_CH_WAIT",       sizeof(subfsm_chwait)/sizeof(subfsm_chwait[0]),     subfsm_chwait},
    {"S_SUBSYS_IDLE",   sizeof(subfsm_idle)/sizeof(subfsm_idle[0]),         subfsm_idle},
};

/* 系统状态机的初始态所对应的几种转移 */
cpo_fsmtrans_t sysfsm_init[] = {
    {EV_SYS_START,      &cpo_fsmpre_null,    &fsm_sys_init_2wait,    &fsm_sys_init_2wait_end,   S_SYSPOINTS_WAIT},
};
/* 系统状态机的S_SYSPOINTS_WAIT所对应的几种转移 */
cpo_fsmtrans_t sysfsm_pointwait[] = {
    {EV_SYS_STOP,           &fsm_sys_wait_2init_pre,    &fsm_sys_wait_2init,    &cpo_fsmend_null,           S_SYS_INIT},
    {EV_SYSPOINTS_ON,       &fsm_sys_wait_2idle_pre,    &fsm_sys_wait_2idle,    &fsm_sys_wait_2idle_end,    S_SYS_IDLE},
};
/* 系统状态机的S_SYS_IDLE所对应的几种转移 */
cpo_fsmtrans_t sysfsm_idle[] = {
    {EV_SYS_STOP,       &fsm_sys_idle_2init_pre,    &fsm_sys_idle_2init,    &fsm_sys_idle_2init_end,    S_SYS_INIT},
};

/* 系统状态机的所有状态的集合
 * 第一个成员代表SYS_INIT状态对应的转移集合
 * 第二个成员代表SYSPOINTS_WAIT状态对应的转移集合
 * 第二个成员代表SYS_IDLE状态对应的转移集合
 * 
 * 这个顺序和cpo_fsm_tp_e中所定义的顺序是一致的
 */
cpo_fsmsets_t cpo_sysfsmset[] = {
    {"S_SYS_INIT",          sizeof(sysfsm_init)/sizeof(sysfsm_init[0]),             sysfsm_init},
    {"S_SYSPOINTS_WAIT",    sizeof(sysfsm_pointwait)/sizeof(sysfsm_pointwait[0]),   sysfsm_pointwait},
    {"S_SYS_IDLE",          sizeof(sysfsm_idle)/sizeof(sysfsm_idle[0]),             sysfsm_idle},
};

/* 系统状态机 */
cpo_fsm_t cpo_sysfsm = {
    .cur_state = S_SYS_INIT,
    .fsmno = SYS_FSM,
    .fsmset = cpo_sysfsmset,
};

enum error_code cpo_fsmpre_null(void *arg)
{
    ((cpo_fsm_t *)arg)->all_stop = false;
    return Success;
}

enum error_code cpo_fsmaction_null(void *arg)
{
    return Success;
}

enum error_code cpo_fsmend_null(void *arg)
{
    return Success;
}

enum error_code fsm_sys_init_2wait_pre(void *arg)
{   
    return Success;
}

enum error_code fsm_sys_init_2wait(void *arg)
{
    data_type_u val;
    point_prop_t *pp;

    ES_PRT_INFO("CPO: Goto SYS fsm: init_2wait... \n");

    /* 冷却塔组的供水蝶阀存在就open it */
    if( (pp = find_point_from_devdp_bynm("CTGSV-C", &cpo_dev->ct_head.ctg_rp)) != NULL ){
        ES_PRT_INFO("CPO: CTGSV-C exist, open it \n");
        val.b = true;
        setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
        sendpoint_2otherapp(pp);

        pthread_mutex_lock(&syspoint_hd.lock);

        add_sp_2clist(pp, &syspoint_hd.clist);
        /* 供水蝶阀的状态反馈点位有好几种类型：
        * 1. 开关状态共用一个点位, 2. 开状态/关状态分开两个点位, 
        * 实际工程中只会出现一种点位，程序中表面上把两种点位都加到状态链表中去了，
        * 所以通过 find_point_from_devdp_bynm()函数找到这个点位才能加
        */
        if( (pp = find_point_from_devdp_bynm("CTGSV-S", &cpo_dev->ct_head.ctg_rp)) != NULL ){
            add_sp_2slist(pp, &syspoint_hd.slist);
        }
        if( (pp = find_point_from_devdp_bynm("CTGSV-OS", &cpo_dev->ct_head.ctg_rp)) != NULL ){
            add_sp_2slist(pp, &syspoint_hd.slist);
        }

        pthread_mutex_unlock(&syspoint_hd.lock);
    }
    /* 冷却塔组的回水蝶阀存在就open it */
    if( (pp = find_point_from_devdp_bynm("CTGRV-C", &cpo_dev->ct_head.ctg_rp)) != NULL ){
        ES_PRT_INFO("CPO: CTGRV-C exist, open it \n");
        val.b = true;
        setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
        sendpoint_2otherapp(pp);

        pthread_mutex_lock(&syspoint_hd.lock);

        add_sp_2clist(pp, &syspoint_hd.clist);

        if( (pp = find_point_from_devdp_bynm("CTGRV-S", &cpo_dev->ct_head.ctg_rp)) != NULL ){
            add_sp_2slist(pp, &syspoint_hd.slist);
        }
        if( (pp = find_point_from_devdp_bynm("CTGRV-OS", &cpo_dev->ct_head.ctg_rp)) != NULL ){
            add_sp_2slist(pp, &syspoint_hd.slist);
        }

        pthread_mutex_unlock(&syspoint_hd.lock);
    }
    syspoint_hd.flag_c = true;
    syspoint_hd.flag_c = true;
    
    return Success;
}

enum error_code fsm_sys_init_2wait_end(void *arg)
{
    /* 重复计时, 定时器TMR_SYSPOINTS_ONWAIT在到时后会去检查一些点位的状态从而发送相应event */
    es_timer_add(TMR_SYSPOINTS_ONWAIT, TMO_SYSPOINTS_ONWAIT, TMO_SYSPOINTS_ONWAIT, cpo_to_sysp_onwait, arg);

    return Success;
}

enum error_code fsm_init_2init_pre(void *arg)
{
    es_timer_del(TMR_WP_OFFWAIT);
    return Success;
}

enum error_code fsm_init_2init(void *arg)
{

    return Success;
}

enum error_code fsm_init_2init_end(void *arg)
{
    if(((cpo_fsm_t *)arg)->fsmevt == EV_SUBSYS_STOP){
        return Success;
    }

    /* 找到还处于非init态的子状态机, 向其发送stop事件 */
    cpo_fsm_t *subfsm_pos;
    list_for_each_entry(subfsm_pos, &cpo_subfsm_hd->list, list){
        if(subfsm_pos->cur_state != S_SUBSYS_INIT){
            send_event_2subfsm(((cpo_fsm_t *)arg)->fsmevt, subfsm_pos->fsmno);
        }
    }

    return Success;
}

enum error_code fsm_sys_wait_2init_pre(void *arg)
{
    es_timer_del(TMR_SYSPOINTS_ONWAIT);
    return Success;
}

enum error_code fsm_sys_wait_2init(void *arg)
{
    struct single_point *pos;
    struct single_point *n;
    point_prop_t *pp;
    data_type_u val;

    ES_PRT_INFO("CPO: Goto SYS fsm: close some points... \n");

    pthread_mutex_lock(&syspoint_hd.lock);

    /* 
     * clist链表都是开关命令相关的点位, true: enable
     * 都是bool型
     */
    list_for_each_entry_safe(pos, n, &syspoint_hd.clist, clist){

        /* 既然点位已经被加到clist链表中了, 就说明点位一定存在 */
        pp = find_point_bydf(pos->pp.dev_key, pos->pp.func);
        if(pp == NULL){
            continue;
        }
        /* close点位 */
        val.b = false;
        setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
        sendpoint_2otherapp(pp);
        list_del(&pos->clist);
        free(pos);
    }
    /* 同时删掉开关命令点位、开关状态点位 */
    list_for_each_entry_safe(pos, n, &syspoint_hd.clist, clist){
        list_del(&pos->clist);
        free(pos);
    }
    list_for_each_entry_safe(pos, n, &syspoint_hd.slist, slist){
        list_del(&pos->slist);
        free(pos);
    }

    pthread_mutex_unlock(&syspoint_hd.lock);

    return Success;
}

enum error_code fsm_sys_wait_2init_end(void *arg)
{
    return Success;
}

enum error_code fsm_sys_wait_2idle_pre(void *arg)
{
    es_timer_del(TMR_SYSPOINTS_ONWAIT);
    return Success;
}

enum error_code fsm_sys_wait_2idle(void *arg)
{
    //TODO: 处理返回失败
    choose_ch_2open();
    /* 系统状态机绑定稍后要启动的子状态机 */
    memcpy(&((cpo_fsm_t *)arg)->bind_dev, &sf_sn, sizeof(sf_sn));
    
    return Success;
}

enum error_code fsm_sys_wait_2idle_end(void *arg)
{
    /* 这里的 arg 是 系统状态机 cpo_fsm_t cpo_sysfsm; */
    /* 向稍后要启动的子状态机发送start事件 */
    send_event_2subfsm(EV_SUBSYS_START, ((cpo_fsm_t *)arg)->bind_dev.ch_sn);

    ((cpo_fsm_t *)arg)->bind_dev.ch_sn = -1;
    ((cpo_fsm_t *)arg)->bind_dev.ct_sn = -1;
    ((cpo_fsm_t *)arg)->bind_dev.cwp_sn = -1;
    ((cpo_fsm_t *)arg)->bind_dev.pchwp_sn = -1;

    return Success;
}

enum error_code fsm_sys_idle_2init_pre(void *arg)
{
    cpo_fsm_t *pos;
    /* 不管有没有这个定时器，都要先执行一遍删除操作 */
    es_timer_del(TMR_SYSIDLE_STOP);

    /* 所有的子状态机都在INIT状态, 才能执行系统状态机的从idle->init的状态转移 */
    list_for_each_entry(pos, &cpo_subfsm_hd->list, list){
        if(pos->cur_state != S_SUBSYS_INIT && pos->all_stop == false){
            ES_PRT_WARN("Sub fsm[%d] is not INIT status, add timer to wait subfsm stop \n", pos->fsmno);
            es_timer_add(TMR_SYSIDLE_STOP, TMO_SYSIDLE_STOP, 0, cpo_to_sysidle_stop, arg);
            return ErrFalse;
        }
    }

    es_timer_del(TMR_WP_OFFWAIT);
    ES_PRT_INFO("SYS fsm: all sub fsm are at S_SUBSYS_INIT status \n");

    return Success;
}

enum error_code fsm_sys_idle_2init(void *arg)
{
    fsm_sys_wait_2init(NULL);
    return Success;
}

enum error_code fsm_sys_idle_2init_end(void *arg)
{
    return Success;
}

enum error_code fsm_init_2ivwpwait(void *arg)
{
    cpo_fsm_t *subfsm_pos = (cpo_fsm_t *)arg;

    data_type_u val;
    point_prop_t *pp;

    /* 打开冷机的几个阀 */
    ch_dev_t *ch_pos;
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){

        /* 找到冷机 */
        if(ch_pos->sn == subfsm_pos->bind_dev.ch_sn){
            val.b = true;
            /* 如果该冷机的冻水侧蝶阀点位存在，就打开, 同时加到链表 */
            set_cmdpoint_ifexist("CHIV-VC", &ch_pos->ch_prop, val, TypeOfVal_BOOL, &subfsm_pos->v_clist);
            /* 如果该冷机的冷却水侧蝶阀点位存在，就打开, 同时加到链表 */
            set_cmdpoint_ifexist("CWIV-VC", &ch_pos->ch_prop, val, TypeOfVal_BOOL, &subfsm_pos->v_clist);
            //TODO: 以后再考虑状态点位
        #if 0
            /* 冷冻水侧蝶阀开状态 */
            if( (pp = find_point_from_devdp_bynm("CHIV-OS", &ch_pos->ch_prop)) != NULL ){
                // TODO: 先加到链表以后备用, 以后可以在一个线程里检测这些点位的状态，并发送事件
                add_sp_2slist(pp, &sf_pos->v_slist);
            }
            /* 冻水侧蝶阀开关状态 */
            if( (pp = find_point_from_devdp_bynm("CHIV-S", &ch_pos->ch_prop)) != NULL ){
                add_sp_2slist(pp, &sf_pos->v_slist);
            }

            /* 冷却水侧蝶阀开状态 */
            if( (pp = find_point_from_devdp_bynm("CWIV-OS", &ch_pos->ch_prop)) != NULL ){
                add_sp_2slist(pp, &sf_pos->v_slist);
            }
            /* 冷却水侧蝶阀开关状态 */
            if( (pp = find_point_from_devdp_bynm("CWIV-CS", &ch_pos->ch_prop)) != NULL ){
                add_sp_2slist(pp, &sf_pos->v_slist);
            }
        #endif
        }
    }

    /* 打开冷却塔的水阀、enable风扇 */
    ct_dev_t *ct_pos;
    ctf_dev_t *ctf_pos;
    data_type_u vsd_val;

    list_for_each_entry(ct_pos, &cpo_dev->ct_head.list, list_ct){
        /* 找到冷却塔 */
        if(ct_pos->sn == subfsm_pos->bind_dev.ct_sn){
            val.b = true;
            /* 冷却塔供水蝶阀开关命令 */
            set_cmdpoint_ifexist("CTSV-C", &ct_pos->ct_prop, val, TypeOfVal_BOOL, &subfsm_pos->ct_clist);
            /* 冷却塔回水蝶阀开关命令 */
            set_cmdpoint_ifexist("CTRV-C", &ct_pos->ct_prop, val, TypeOfVal_BOOL, &subfsm_pos->ct_clist);
            
            /* enable这个塔的所有风扇 */
            list_for_each_entry(ctf_pos, &ct_pos->list_ctf_hd, list){
                set_cmdpoint_ifexist("CTF-C", &ctf_pos->ctf_prop, val, TypeOfVal_BOOL, &subfsm_pos->ct_clist);

                /* enable变频风扇, 频率默认设置为最大 */
                if(ct_pos->ct_prop.is_vsd == true){
                    set_cmdpoint_ifexist("CTF-VSD-C", &ctf_pos->ctf_prop, val, TypeOfVal_BOOL, &subfsm_pos->ct_clist);
                    vsd_val.d = ct_pos->ct_prop.vsd_max;
                    set_cmdpoint_ifexist("CTF-VSD", &ctf_pos->ctf_prop, vsd_val, TypeOfVal_DOUBLE, NULL);
                }
            }
            //TODO: 以后再考虑状态点位
        }
    }

    //TODO: 将上述蝶阀的操作和下列水泵的操作分成两个状态, 就不用睡眠了, 否则睡眠时间过长影响其他状态机的接收
    sleep(2);

    /* 打开冷机对应的冷冻、冷却水泵 */
    wp_dev_t *pchwp_pos;

    list_for_each_entry(pchwp_pos, &cpo_dev->pchwp_head.list, list){
        /* 找到初级冷冻水泵 */
        if(pchwp_pos->sn == subfsm_pos->bind_dev.pchwp_sn){
            val.b = true;
            set_cmdpoint_ifexist("PCHWP-C", &pchwp_pos->wp_prop, val, TypeOfVal_BOOL, &subfsm_pos->wp_clist);
            /* 如果水泵状态点位存在, 就加到状态链表中 */
            if( (pp = find_point_from_devdp_bynm("PCHWP-S", &pchwp_pos->wp_prop)) != NULL ){
                add_sp_2slist(pp, &subfsm_pos->wp_slist);
            }

            /* 如果变频, 频率默认设置为最大 */
            if(pchwp_pos->wp_prop.is_vsd == true){
                set_cmdpoint_ifexist("PCHWP-VSD-C", &pchwp_pos->wp_prop, val, TypeOfVal_BOOL, &subfsm_pos->wp_clist);
                vsd_val.d = pchwp_pos->wp_prop.vsd_max;
                set_cmdpoint_ifexist("PCHWP-VSD", &pchwp_pos->wp_prop, vsd_val, TypeOfVal_DOUBLE, NULL);
            }
        }
    }

    wp_dev_t *cwp_pos;
    list_for_each_entry(cwp_pos, &cpo_dev->cwp_head.list, list){
        /* 找到冷却水泵 */
        if(cwp_pos->sn == subfsm_pos->bind_dev.cwp_sn){
            val.b = true;
            set_cmdpoint_ifexist("CWP-C", &cwp_pos->wp_prop, val, TypeOfVal_BOOL, &subfsm_pos->wp_clist);

            /* 如果水泵状态点位存在，就加到状态链表中 */
            if( (pp = find_point_from_devdp_bynm("CWP-S", &cwp_pos->wp_prop)) != NULL ){
                add_sp_2slist(pp, &subfsm_pos->wp_slist);
            }

            /* 如果变频, 频率默认设置为最大 */
            if(cwp_pos->wp_prop.is_vsd == true){
                set_cmdpoint_ifexist("CWP-VSD-C", &cwp_pos->wp_prop, val, TypeOfVal_BOOL, &subfsm_pos->wp_clist);
                vsd_val.d = cwp_pos->wp_prop.vsd_max;
                set_cmdpoint_ifexist("CWP-VSD", &cwp_pos->wp_prop, vsd_val, TypeOfVal_DOUBLE, NULL);
            }
        }
    }
    return Success;
}

enum error_code fsm_init_2ivwpwait_end(void *arg)
{
    es_timer_add(TMR_WP_ONWAIT, TMO_WP_ONWAIT, TMO_WP_ONWAIT, cpo_to_wp_onwait, arg);
    return Success;
}

enum error_code fsm_ivwpwait_2chwait_pre(void *arg)
{
    /* 初级冷冻水泵的运行时间不能小于10min */
    wp_dev_t *pchwp_pos;
    list_for_each_entry(pchwp_pos, &cpo_dev->pchwp_head.list, list){
        /* NOTE: 有时候冷机没有绑定水泵, 此时((cpo_fsm_t *)arg)->bind_dev.pchwp_sn==-1 */
        if(pchwp_pos->sn == ((cpo_fsm_t *)arg)->bind_dev.pchwp_sn){
            if(pchwp_pos->wp_prop.curr_rt < T_WP_RT_BF_CH){
//                return ErrFalse;
            }
        }
    }
    /* 冷却水泵的运行时间不能小于10min */
    wp_dev_t *cwp_pos;
    list_for_each_entry(cwp_pos, &cpo_dev->cwp_head.list, list){
        if(cwp_pos->sn == ((cpo_fsm_t *)arg)->bind_dev.cwp_sn){
            if(cwp_pos->wp_prop.curr_rt < T_WP_RT_BF_CH){
//                return ErrFalse;
            }
        }
    }
    /* 只能在Success前del timer */
    es_timer_del(TMR_WP_ONWAIT);
    return Success;
}

enum error_code fsm_ivwpwait_2chwait(void *arg)
{
    /* open 冷机 */
    ch_dev_t *ch_pos;
    data_type_u val;
    val.b = true;

    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        if(ch_pos->sn == ((cpo_fsm_t *)arg)->bind_dev.ch_sn){
            set_cmdpoint_ifexist("CH-C", &ch_pos->ch_prop, val, TypeOfVal_BOOL, NULL);
        }
    }

    return Success;
}

enum error_code fsm_ivwpwait_2chwait_end(void *arg)
{
    es_timer_add(TMR_CH_ONWAIT, TMO_CH_ONWAIT, TMO_CH_ONWAIT, cpo_to_ch_onwait, arg);
    return Success;
}

enum error_code fsm_ivwpwait_2init_pre(void *arg)
{
    es_timer_del(TMR_WP_ONWAIT);
    es_timer_del(TMR_CH_OFFWAIT);

    //TODO: 判断水泵运行时间 > 水泵关机前最小运行时间

    return Success;
}

enum error_code fsm_ivwpwait_2init(void *arg)
{
    /* close 水泵 */
    struct single_point *sp_pos, *nsp;
    point_prop_t *pp;
    data_type_u val;

    val.b = false;

    list_for_each_entry_safe(sp_pos, nsp, &((cpo_fsm_t *)arg)->wp_clist, clist){
        pp = find_point_bydf(sp_pos->pp.dev_key, sp_pos->pp.func);
        if(pp != NULL){
            setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
            sendpoint_2otherapp(pp);
        }
        // TODO: 删节点操作放到一个定时器中, 待确认水泵关闭后, 再清理链表
        list_del(&sp_pos->clist);
        free(sp_pos);
    }

#if 1 // TODO: 开一个定时器, 确认水泵关闭后再清理链表, 和上面的TODO是一个意思

    /* clean up 状态链表 */
    list_for_each_entry_safe(sp_pos, nsp, &((cpo_fsm_t *)arg)->wp_slist, slist){
        list_del(&sp_pos->slist);
        free(sp_pos);
    }

#endif
    /* 实际工程改成10min, 要等水流停止 */
    sleep(1);

    /* close 冷却塔风机和阀, ct_list在添加链表时，风机是头插法后插入的, 
     * 所以此时关闭时，风机是先先关闭的
     */
    list_for_each_entry(sp_pos, &((cpo_fsm_t *)arg)->ct_clist, clist){
        pp = find_point_bydf(sp_pos->pp.dev_key, sp_pos->pp.func);
        if(pp != NULL){
            setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
            sendpoint_2otherapp(pp);
        }
    }

    /* close 冷机的冷冻侧和冷却侧水阀 */
    list_for_each_entry(sp_pos, &((cpo_fsm_t *)arg)->v_clist, clist){
        pp = find_point_bydf(sp_pos->pp.dev_key, sp_pos->pp.func);
        if(pp != NULL){
            setval_2devdp_bypp(pp, val, TypeOfVal_BOOL);
            sendpoint_2otherapp(pp);
        }
    }

    return Success;
}

enum error_code fsm_ivwpwait_2init_end(void *arg)
{
    es_timer_add(TMR_WP_OFFWAIT, TMO_WP_OFFWAIT, TMO_WP_OFFWAIT, cpo_to_wp_offwait, arg);
    return Success;
}

enum error_code fsm_chwait_2idle_pre(void *arg)
{
    es_timer_del(TMR_CH_ONWAIT);
    return Success;
}

enum error_code fsm_chwait_2ivwpwait_pre(void *arg)
{
    es_timer_del(TMR_CH_ONWAIT);

    ch_dev_t *ch_pos;

    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        /* 找到对应的冷机 */
        if(((cpo_fsm_t *)arg)->fsmno == ch_pos->sn){
            break;
        }
    }

    es_timer_del(TMR_CH_BF_CLOSE);

    /* 冷机满足最小运行时间 */
    if(ch_pos->ch_prop.curr_rt >= T_CH_RT_BF_CLOSE){
        return Success;
    }

    /* 一次性计时, 到时后, cpo_ch_bf_close()再发送一次 stop 事件 */
    es_timer_add(TMR_CH_BF_CLOSE, T_CH_RT_BF_CLOSE - ch_pos->ch_prop.curr_rt + 1, 0, cpo_ch_bf_close, arg);  
    return ErrFalse;
}

enum error_code fsm_chwait_2ivwpwait(void *arg)
{
    /* close 冷机 */
    ch_dev_t *ch_pos;
    data_type_u val;
    val.b = false;

    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        if(ch_pos->sn == ((cpo_fsm_t *)arg)->fsmno){
            ES_PRT_INFO("CPO: Sub fsm NO.[%d]: close ch \n", ch_pos->sn);
            set_cmdpoint_ifexist("CH-C", &ch_pos->ch_prop, val, TypeOfVal_BOOL, NULL);
        }
    }
    return Success;
}

enum error_code fsm_chwait_2ivwpwait_end(void *arg)
{
    es_timer_add(TMR_CH_OFFWAIT, TMO_CH_OFFWAIT, TMO_CH_OFFWAIT, cpo_to_ch_offwait, arg);
    return Success;
}

enum error_code fsm_idle_2chwait_end(void *arg)
{
    send_event_2subfsm(((cpo_fsm_t *)arg)->fsmevt, ((cpo_fsm_t *)arg)->fsmno);
    return Success;
}

/* 状态机
 * @event: 事件类型
 * @fsm: 系统状态机, 还是子状态机
 */
void fsm_do(cpo_ev_tp_e event, cpo_fsm_t *fsm)
{
    char fsm_name[20];
    if(fsm->fsmno == SYS_FSM){
        strcpy(fsm_name, "SYS fsm");
    }else{
        strcpy(fsm_name, "SUB fsm");
    }

    int i;
    /* 所有状态的状态集合 */
    cpo_fsmsets_t *fsmset = fsm->fsmset;
    /* 找到当前状态下的状态转移集合 */
    cpo_fsmtrans_t *fsmtrans = fsmset[fsm->cur_state].cpo_fsm;
    /* 找到当前状态下有几个事件分支 */
    int	ret = fsmset[fsm->cur_state].num;

    for(i=0; i<ret; i++){
		if(event == (int)fsmtrans[i].event){
			break;
		}
	}

    if(i>=ret){
//		ES_PRT_INFO("CPO: state[%s] does not process event[%d] \n", fsmset[fsm->cur_state].name, event);
	}else{
        /* 1 预处理 */
        ret = (*fsmtrans[i].pre)(fsm);
        /* 1.1 预处理成功 */
        if(ret == Success){
            /* 2 执行 */
            ret = (*fsmtrans[i].action)(fsm);
            /* 2.1 执行成功 */
            if(ret == Success){
                /* 2.1.1 切换状态 */
                fsm->pre_state = fsm->cur_state;
                fsm->cur_state = fsmtrans[i].next_state;
                /* 3 收尾 */
                ret = (*fsmtrans[i].end)(fsm);
                /* 3.1 收尾失败 */
                if(ret != Success){
                    ES_PRT_ERROR("CPO: %s[%d] end error: goto status[%s], event[%d] \n", fsm_name, fsm->fsmno, fsmset[fsm->cur_state].name, event);
                }else{
                    ES_PRT_INFO("CPO: %s[%d] from state[%s] to state[%s] \n", fsm_name, fsm->fsmno, fsmset[fsm->pre_state].name, fsmset[fsm->cur_state].name);
                }
            /* 2.2 执行失败, 状态不变 */
            }else{
                ES_PRT_ERROR("CPO: %s[%d] action error: cur status[%s], event[%d] \n", fsm_name, fsm->fsmno, fsmset[fsm->cur_state].name, event);
            }
        /* 1.2 预处理失败，状态不变 */
        }else{
            ES_PRT_WARN("CPO: %s[%d] stay old status[%s], event[%d] \n", fsm_name, fsm->fsmno, fsmset[fsm->cur_state].name, event);
        }
    }

}

/* 向系统状态机发送事件
 * @tp: 事件类型
 */
void send_event_2sysfsm(cpo_ev_tp_e tp)
{
    cpo_msg_t tx_msg;
    tx_msg.mtype = CPO_MQTP_EVENT;
    tx_msg.fsmevt = tp;
    tx_msg.fsmno = SYS_FSM;
    *(int*)tx_msg.data = 100;   // 目前无实际意义, debug用

    ES_PRT_INFO("send event[%d] to sys fsm \n", tp);

    if( msgsnd(mq_id, (void*)&tx_msg, sizeof(tx_msg)-sizeof(tx_msg.mtype), 0) < 0){
        ES_PRT_ERROR("%s, mtpye = %d, event = %d, fsmno = %d \n", strerror(errno), CPO_MQTP_EVENT, tp, SYS_FSM);
    }

}

/* 向子状态机发送事件
 * @tp: 事件类型
 * @fsmno: 子状态机号
 */
void send_event_2subfsm(cpo_ev_tp_e tp, int fsmno)
{
    cpo_msg_t tx_msg;
    tx_msg.mtype = CPO_MQTP_EVENT;
    tx_msg.fsmevt = tp;
    tx_msg.fsmno = fsmno;
    *(int*)tx_msg.data = 100;   // 目前无实际意义, debug用

    ES_PRT_INFO("send event[%d] to sub fsm NO[%d] \n", tp, fsmno);

    if( msgsnd(mq_id, (void*)&tx_msg, sizeof(tx_msg)-sizeof(tx_msg.mtype), 0) < 0){
        ES_PRT_ERROR("%s, mtpye = %d, event = %d, fsmno = %d \n", strerror(errno), CPO_MQTP_EVENT, tp, SYS_FSM);
    }
}

/* 检查系统总启动命令的状态, 状态有变化就发送启停事件 */
void ck_c_call_state()
{
    static bool old_state = false;
    data_type_u val;

    /* C_CALL点位必须存在 */
    read_sysvp_byname(C_CALL, &val);

    /* 状态变化时才发送事件 */
    /* NOTE: 系统启、停的时间间隔默认足够长, 暂时不做判断保护 */
    if(old_state == false && val.b == true){
        send_event_2sysfsm(EV_SYS_START);
    }else if(old_state == true && val.b == false){
        send_event_2subfsm(EV_SYS_STOP, -1);
        send_event_2sysfsm(EV_SYS_STOP);
    }

    old_state = val.b;
}

/* 轮询检查和状态相关的点位, 根据不同情况发送不同事件 */
void *cpo_state_ck_thread(void *arg)
{
    while(1){
        sleep(2);
        /* 等待有设备被添加 */
        if(cpo_dev->add_dev == false){
            continue;
        }
        ck_c_call_state();
    }

    return NULL;
}